`timescale 1ns / 1ps
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Company:			Microsystems Design Lab (MDL)
//					The Pennsylvania State University
// Engineer: 		Yang Xiao
//
// Create Date:		6/13/2012
// Design Name: 	Descriptor
// Module Name:     OA Smart buffer
// Project Name:	Future Store Analytics
// Target Devices: 	
// Tool versions:
// Description:		This module provides parallel access to integral image pixels.
//					The module is responsible for the following:
//					1. Loading/registering image width and height
//					2. Storing incoming integral image in the buffer
//					3. Honoring requests for pixels in steps, depending on which octaves are currently active
//					4. Progressing the sliding window and managing the replenishment of rows
//
// Dependencies:
//
// Revision:
// Revision 1.0 - File Created
//
// Additional Comments:
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
`include "parameter.vh"
module oa_smart_buffer (
	clk,
	
	// Interface to top level
	new_frame,
	new_config,
	image_height,
	image_width,
	src_valid_in,
	data_in,
	
	
	// Address from OA
	row_t,			
	row_m,			
	row_b,			
	col_l,			
	col_m,			
	col_r,			
	haar_addr_valid,
		
	// < Enter Output Ports in Alphabetical Order >
	// Interface to top level
	dst_rdy_in,
	data_valid_out,	
	data_1,	
	data_2,	
	data_3,	
	data_4,	
	data_5,	
	center_fetch_done
);

	//--------------------------
	// Parameters
	//--------------------------
	parameter	NUM_ROWS	= 256;
	parameter	NUM_COLS	= 1024;
	
	//--------------------------
	// Input Ports
	//--------------------------
	input										clk;
	
	input										new_frame;			// --- new frame indicator ---
	input										new_config;			// --- new config indicator ---
	input			[`INT_IMG_AWIDTH : 0]		image_height;
	input			[`INT_IMG_AWIDTH : 0]		image_width;
	input										src_valid_in;
	input			[`INT_IMG_DWIDTH-1 : 0]		data_in;
	
	input	signed	[`INT_IMG_AWIDTH+1 : 0]		row_t;			
	input	signed	[`INT_IMG_AWIDTH+1 : 0]		row_m;			
	input	signed	[`INT_IMG_AWIDTH+1 : 0]		row_b;			
	input	signed	[`INT_IMG_AWIDTH+1 : 0]		col_l;			
	input	signed	[`INT_IMG_AWIDTH+1 : 0]		col_m;			
	input	signed	[`INT_IMG_AWIDTH+1 : 0]		col_r;			
	input								haar_addr_valid;
		

	//--------------------------
	// Output Ports
	//--------------------------
	output										dst_rdy_in;
	output										data_valid_out;	
	output			[`INT_IMG_DWIDTH-1 : 0]		data_1;	
	output			[`INT_IMG_DWIDTH-1 : 0]		data_2;	
	output			[`INT_IMG_DWIDTH-1 : 0]		data_3;	
	output			[`INT_IMG_DWIDTH-1 : 0]		data_4;	
	output			[`INT_IMG_DWIDTH-1 : 0]		data_5;	
	output										center_fetch_done;

	//--------------------------
	// Bidirectional Ports
	//--------------------------

	///////////////////////////////////////////////////////////////////
	// Begin Design
	///////////////////////////////////////////////////////////////////
	//-------------------------------------------------
	// Signal Declarations: local params
	//-------------------------------------------------
	// WRITE_FSM states
	localparam	WR_IDLE	 		= 8'd0;
	localparam	WR_INIT	 		= 8'd1;
	localparam	WR_FILL	 		= 8'd2;

	// READ_FSM states
	localparam	RD_IDLE	 		= 8'd0;
	localparam	RD_WAIT	 		= 8'd1;
	localparam	RD_FIRST 		= 8'd2;
	localparam	RD_SECOND		= 8'd3;
	localparam	RDBK_FIRST		= 8'd4;
	localparam	RDBK_SECOND		= 8'd5;
	
	
	//-------------------------------------------------
	// Signal Declarations: reg
	//-------------------------------------------------
	// --- Register signals ---
	reg		[`INT_IMG_AWIDTH : 0]				image_width_r				= 1023	;			// Image width
	reg		[`INT_IMG_AWIDTH : 0]				image_height_r				= 1023	;			// Image height
	reg											dst_rdy_in_r				= 0	;				// Destination ready (Ingress) 
	
	reg		[7 : 0]								wr_fsm						= 0	;
	reg		[`INT_IMG_AWIDTH-1 : 0]				iib_addra_r[NUM_ROWS-1 : 0]	   	;				// Integral image buffer address bus for port A
	reg		[`INT_IMG_AWIDTH-1 : 0]				iib_addrb_r[NUM_ROWS-1 : 0]	   	;				// Integral image buffer address bus for port B	
	reg		[NUM_ROWS-1 : 0]					iib_wea_r					= 0	;				// Integral image buffer write enable for port A
	reg		[NUM_ROWS-1 : 0]					iib_web_r					= 0	;				// Integral image buffer write enable for port B
	reg		[`INT_IMG_DWIDTH*NUM_ROWS-1 : 0]	iib_dina_r					= 0	;				// Integral image data in bus for port A
	reg		[`INT_IMG_DWIDTH*NUM_ROWS-1 : 0]	iib_dinb_r					= 0	;				// Integral image data in bus for port B
	reg		[`INT_IMG_AWIDTH-1 : 0]				global_row_r				= 0	;
	reg		[`INT_IMG_AWIDTH-1 : 0]				global_col_r				= 0	;

	
	reg		[7 : 0]								rd_fsm						= 0	;
	reg		[`INT_IMG_AWIDTH-1 : 0]				rd_addra_r[NUM_ROWS-1:0]	   	;				// Integral image buffer address bus for port A
	reg		[`INT_IMG_AWIDTH-1 : 0]				rd_addrb_r[NUM_ROWS-1:0]	   	;				// Integral image buffer address bus for port B	
	reg		[`INT_IMG_AWIDTH-1 : 0]				row_t_r						= 0	;			
	reg		[`INT_IMG_AWIDTH-1 : 0]				row_m_r						= 0	;			
	reg		[`INT_IMG_AWIDTH-1 : 0]				row_b_r						= 0	;			
	reg		[`INT_IMG_AWIDTH-1 : 0]				col_l_r						= 0	;			
	reg		[`INT_IMG_AWIDTH-1 : 0]				col_m_r						= 0	;			
	reg		[`INT_IMG_AWIDTH-1 : 0]				col_r_r						= 0	;		

	reg											data_valid_out_r			= 0	;	
	reg		[`INT_IMG_DWIDTH-1 : 0]				data_1_r					= 0	;	
	reg		[`INT_IMG_DWIDTH-1 : 0]				data_2_r					= 0	;	
	reg		[`INT_IMG_DWIDTH-1 : 0]				data_3_r					= 0	;	
	reg		[`INT_IMG_DWIDTH-1 : 0]				data_4_r					= 0	;	
	reg		[`INT_IMG_DWIDTH-1 : 0]				data_5_r					= 0	;	
	reg											center_fetch_done_r 		= 0	;

	//-------------------------------------------------
	// Signal Declarations: wire
	//-------------------------------------------------
	wire	[`INT_IMG_DWIDTH*NUM_ROWS-1 : 0]	iib_douta_w;				// Integral image buffer data out bus for port A
	wire	[`INT_IMG_DWIDTH*NUM_ROWS-1 : 0]	iib_doutb_w;				// Integral image buffer data out bus for port B
	
	wire	[`INT_IMG_AWIDTH*NUM_ROWS-1 : 0]	iib_addra_w;				// Integral image address bus for port A
	wire	[`INT_IMG_AWIDTH*NUM_ROWS-1 : 0]	iib_addrb_w;				// Integral image address bus for port B

	wire	[`INT_IMG_DWIDTH-1 : 0]				douta_w[NUM_ROWS-1 : 0];
	wire	[`INT_IMG_DWIDTH-1 : 0]				doutb_w[NUM_ROWS-1 : 0];
	//---------------------------------------------------------------
	// Assignments
	//---------------------------------------------------------------
	
	generate
		genvar i;	
		for (i = 0; i < NUM_ROWS; i = i + 1) begin : BUFFER_ADDR_BUS
			assign iib_addra_w[(i+1)*`INT_IMG_AWIDTH-1:i*`INT_IMG_AWIDTH]		= (iib_wea_r[i]) ? iib_addra_r[i] : rd_addra_r[i];
			assign iib_addrb_w[(i+1)*`INT_IMG_AWIDTH-1:i*`INT_IMG_AWIDTH]		= (iib_web_r[i]) ? iib_addrb_r[i] : rd_addrb_r[i];
			assign douta_w[i]	= iib_douta_w[(i+1)*`INT_IMG_DWIDTH-1:i*`INT_IMG_DWIDTH];
			assign doutb_w[i]	= iib_doutb_w[(i+1)*`INT_IMG_DWIDTH-1:i*`INT_IMG_DWIDTH];
		end
	endgenerate
	
	assign dst_rdy_in			=  dst_rdy_in_r;
	assign data_valid_out		=  data_valid_out_r;	
	assign data_1				=  data_1_r;	
	assign data_2				=  data_2_r;	
	assign data_3				=  data_3_r;	
	assign data_4				=  data_4_r;	
	assign data_5				=  data_5_r;
	assign center_fetch_done	=  center_fetch_done_r;	
	//---------------------------------------------------------------
	// Instantiations
	//---------------------------------------------------------------
	integral_image_buffer
	#(
		.PXL_WIDTH(`INT_IMG_DWIDTH),
		.NUM_COLUMNS(NUM_COLS),
		.NUM_ROWS(NUM_ROWS),
		.ADDR_WIDTH(`INT_IMG_AWIDTH)
	) integral_image_buffer_i
	(
		.clk(clk),
		
		.addra(iib_addra_w),
		.wea(iib_wea_r),
		.dina(iib_dina_r),
		
		.addrb(iib_addrb_w),
		.web(iib_web_r),
		.dinb(iib_dinb_r),
		
		.douta(iib_douta_w),
		.doutb(iib_doutb_w)
	);
	
	//---------------------------------------------------------------
	// Sequential Logic
	//---------------------------------------------------------------
	integer row_index;
	// --- FSM for buffer filling ---
	always @ (posedge clk) begin
		for (row_index = 0; row_index < NUM_ROWS; row_index = row_index + 1) begin
			iib_addra_r[row_index]		<= 0;
			iib_addrb_r[row_index]		<= 0;
			rd_addra_r[row_index]		<= 0;
			rd_addrb_r[row_index]		<= 0;
		end
		iib_wea_r						<= {NUM_ROWS{1'b0}};
		iib_web_r						<= {NUM_ROWS{1'b0}};
		iib_dina_r						<= 0;
		iib_dinb_r						<= 0;
		
		wr_fsm							<= wr_fsm;
		dst_rdy_in_r					<= 0;
					
		rd_fsm							<= rd_fsm;

		// Update image height and width
		if (new_config) begin
			image_height_r				<= image_height;
			image_width_r				<= image_width;
		end
					
		
		case (wr_fsm)
			WR_IDLE: begin		
				if (new_frame) begin
					// Reset integral image buffer
					for (row_index = 0; row_index < NUM_ROWS; row_index = row_index + 1) begin
						iib_addra_r[row_index]			<= 0;
						iib_addrb_r[row_index]			<= 1;
					end
					
					iib_wea_r							<= {NUM_ROWS{1'b1}};
					iib_web_r							<= {NUM_ROWS{1'b1}};
					
					iib_dina_r							<= 0;
					iib_dinb_r							<= 0;
					
					wr_fsm								<= WR_INIT;
				end
			end

			WR_INIT: begin
				// Initialize with all 0's
				if (iib_addrb_r[0] >= image_width_r-1) begin
					dst_rdy_in_r						<= 1;
					wr_fsm								<= WR_FILL;
				end
				else begin
					for (row_index = 0; row_index < NUM_ROWS; row_index = row_index + 1) begin
						iib_addra_r[row_index]			<= iib_addra_r[row_index] + 2;
						iib_addrb_r[row_index]			<= iib_addrb_r[row_index] + 2;
					end
					
					iib_wea_r							<= {NUM_ROWS{1'b1}};
					iib_web_r							<= {NUM_ROWS{1'b1}};
					
					iib_dina_r							<= 0;
					iib_dinb_r							<= 0;
				end
			end
				
				
			WR_FILL: begin
				dst_rdy_in_r							<= 1;
				
				if (src_valid_in) begin
					// Update global counter					
					if (global_row_r ==	image_height_r-1 && global_col_r == image_width_r-1) begin
						dst_rdy_in_r					<= 0;
						wr_fsm							<= WR_IDLE;
					end
					else if (global_col_r == image_width_r-1) begin
						global_row_r					<= global_row_r + 1;
						global_col_r					<= 0;
					end
					else begin
						global_col_r					<= global_col_r + 1;
					end
				
					// Store the date
					iib_addra_r[global_row_r[7 : 0]]	<= global_col_r;
					iib_wea_r							<= 1 << global_row_r[7 : 0];
					iib_dina_r							<= {NUM_ROWS{data_in}};
				end
			end

			default: begin
				wr_fsm									<= WR_IDLE;
			end
		endcase

		// --- FSM for reading data ---
		center_fetch_done_r								<= 0;
		rd_fsm											<= rd_fsm;
		data_valid_out_r								<= 0;	
		data_1_r	  									<= 0;
		data_2_r	  									<= 0;
		data_3_r	  									<= 0;
		data_4_r	  									<= 0;
		data_5_r	  									<= 0;


		case(rd_fsm)
			RD_IDLE: begin		// rd_fsm = 0
				if (dst_rdy_in_r == 1) begin
					rd_fsm								<= RD_WAIT;
				end
			end

			RD_WAIT: begin		// rd_fsm = 1
				if (haar_addr_valid) begin
					rd_fsm								<= RD_FIRST;
					row_m_r								<= row_m;
					col_m_r								<= col_m;
					// Check top row
					if (row_t < 0) begin
						row_t_r							<= 0;
					end
					else begin
						row_t_r							<= row_t;
					end	

					// Check left column
					if (col_l < 0) begin
						col_l_r							<= 0;
					end
					else begin
						col_l_r							<= col_l;
					end
					
					// Check bottom row
					if (row_b > image_height-1) begin
						row_b_r							<= image_height - 1;
					end
					else begin
						row_b_r							<= row_b;
					end

					// Check right column
					if (col_r > image_width-1) begin
						col_r_r							<= image_width - 1;
					end
					else begin
						col_r_r							<= col_r;
					end
				end 
			end

			RD_FIRST: begin		// rd_fsm = 2 
				if((global_row_r > row_b_r) || (global_row_r == row_b_r && global_col_r >= col_r_r)) begin
					rd_fsm								<= RD_SECOND;
					rd_addra_r[row_t_r[7 : 0]]			<= col_l_r;					
					rd_addrb_r[row_t_r[7 : 0]]			<= col_m_r;					
					rd_addra_r[row_m_r[7 : 0]]			<= col_l_r;					
					rd_addra_r[row_b_r[7 : 0]]			<= col_l_r;					
					rd_addrb_r[row_b_r[7 : 0]]			<= col_m_r;					
				end
			end

			RD_SECOND: begin	// rd_fsm = 3
				rd_fsm									<= RDBK_FIRST;
				rd_addra_r[row_t_r[7 : 0]]				<= col_r_r;					
				rd_addra_r[row_m_r[7 : 0]]				<= col_r_r;					
				rd_addra_r[row_b_r[7 : 0]]				<= col_r_r;				
			end
		
			RDBK_FIRST: begin	// rd_fsm = 4
				rd_fsm									<= RDBK_SECOND;
				data_valid_out_r						<= 1;
				if (row_t >= 0 && col_l >= 0) begin
					data_1_r							<= douta_w[row_t_r[7 : 0]];
				end
				else begin
					data_1_r							<= 0;
				end

				if (row_t >= 0 && col_m >= 0) begin
					data_2_r							<= doutb_w[row_t_r[7 : 0]];
				end
				else begin
					data_2_r							<= 0;
				end
				
				if (row_m >= 0 && col_l >= 0) begin
					data_3_r							<= douta_w[row_m_r[7 : 0]];
				end
				else begin
					data_3_r							<= 0;
				end
				
				if (col_l >= 0) begin
					data_4_r							<= douta_w[row_b_r[7 : 0]];
				end
				else begin
					data_4_r							<= 0;
				end
				
				if (col_m >= 0) begin	
					data_5_r							<= doutb_w[row_b_r[7 : 0]];
				end
				else begin
					data_5_r							<= 0;
				end
			end
	
			RDBK_SECOND: begin	// rd_fsm = 5
				center_fetch_done_r						<= 1;
				data_valid_out_r						<= 1;	
				if (row_t >= 0) begin
					data_1_r  							<= douta_w[row_t_r[7 : 0]];
				end
				else begin
					data_1_r  							<= 0;
				end
				if (row_m >= 0) begin
					data_2_r	  						<= douta_w[row_m_r[7 : 0]];
				end
				else begin
					data_2_r	  						<= 0;
				end
				data_3_r	  							<= douta_w[row_b_r[7 : 0]];
				data_4_r	  							<= 0;
				data_5_r	  							<= 0;
				
				if (haar_addr_valid) begin
					rd_fsm								<= RD_FIRST;
					row_m_r								<= row_m;
					col_m_r								<= col_m;
					// Check top row
					if (row_t < 0) begin
						row_t_r							<= 0;
					end
					else begin
						row_t_r							<= row_t;
					end	

					// Check left column
					if (col_l < 0) begin
						col_l_r							<= 0;
					end
					else begin
						col_l_r							<= col_l;
					end
					
					// Check bottom row
					if (row_b > image_height-1) begin
						row_b_r							<= image_height - 1;
					end
					else begin
						row_b_r							<= row_b;
					end

					// Check right column
					if (col_r > image_width-1) begin
						col_r_r							<= image_width - 1;
					end
					else begin
						col_r_r							<= col_r;
					end
				end
				else begin
					rd_fsm								<= RD_WAIT;
				end 
			end
			default: begin
				rd_fsm									<= RD_IDLE;
			end
		endcase		
	end
endmodule
